/**
 *  \file I3OverlayNetwork_triggerEvents.cpp
 *  \brief This file contains the functions of the I3OverlayNetwork class
 *  that handles i3 trigger events.
 */

#include "OCD_include.h"

#include "i3_debug.h"
#include "I3Functor.h"
#include "I3AddressBook.h"
#include "I3Id.h"
#include "I3Key.h"
#include "I3ConfigFile.h"
#include "I3TriggerConfigFile.h"
#include "PublicTrigger.h"
#include "I3OCDStateInfo.h"
#include "I3Params.h"
#include "I3Packet.h"
#include "I3TimerEvent.h"
#include "I3Trigger.h"
#include "I3OverlayNetwork.h"
#include "I3ClientContext.h"
#include "I3Packet.h"

#ifndef _WIN32
	#include <sys/socket.h>
	#include <arpa/inet.h>
	#include <netinet/in.h>
#endif

#include <errno.h>
#include <string.h>
#include <pthread.h>

#include <string>

extern "C" {
    #include "i3.h"
    #include "i3_api.h"
    #include "i3_id.h"
    #include "i3_addr.h"
    #include "i3_trigger.h"
    #include "i3_client_api.h"
}

#include "i3CommonHeaders.h"


void dummy_insertedTrigger(cl_trigger*, void*, void*);
void dummy_triggerNotFound(void *ctx_data, void *data, void* fun_ctx);
void dummy_constraint_failed(cl_trigger*, void*, void*);
void dummy_receive_packet(cl_trigger*, void*, void*);
void dummy_trigger_refresh_failed(cl_trigger*, void*, void*);

using namespace std;


extern "C" void proxy_trigger_inserted();


/**
  * This function acts as a bridge between the C i3 API and the C++ generic proxy code 
  * @param data This argument will be cast to an object of type I3Trigger.
  * Type safety takes a backseat here.
  */
void dummy_insertedTrigger(cl_trigger *t, void *data, void* fun_ctx) {
    
    I3Trigger *cbkTrig = (I3Trigger *) fun_ctx;
    cbkTrig->doCallbacks(CL_CBK_TRIGGER_INSERTED, NULL);
}

/**
  * This function acts as a bridge between the C i3 API and the C++ generic proxy code 
  * @param ctx_data is cast to ID *.
  * @param fun_ctx is cast to I3OverlayNetwork*
  * Type safety takes a backseat here.
  */
void dummy_triggerNotFound(void *ctx_data, void *data, void* fun_ctx) {
    
    ID *tmpId = (ID *) ctx_data;
    I3OverlayNetwork *tmpOverlay = (I3OverlayNetwork *) fun_ctx;
    tmpOverlay->processTriggerNotFound(tmpId);
    
}
void dummy_receive_packet(cl_trigger *t, void *data, void *fun_ctx) {

    I3Trigger *cbkTrig = (I3Trigger *) fun_ctx;
    cbkTrig->doCallbacks(CL_CBK_RECEIVE_PACKET, (cbk_packet*) data);

}

void dummy_trigger_refresh_failed(cl_trigger *t, void *data, void *fun_ctx) {

    char tmpBuf[MAX_PRINT_BUF];
    I3_PRINT_INFO1(I3_INFO_LEVEL_WARNING, "Refresh failed for trigger with id ", sprintf_i3_id(tmpBuf, &t->t->id));
}

void dummy_constraint_failed(cl_trigger *t, void *data, void* fun_ctx) {
    char tmpBuf[MAX_PRINT_BUF];
    I3_PRINT_INFO1(I3_INFO_LEVEL_WARNING, "Contraint failed for trigger with id ", sprintf_i3_id(tmpBuf, &t->t->id));

}

/**
  * This function is called when a trigger (public or private) has been inserted (i.e. when we get the ack).
  * @param trig The trigger for which insertion ack was received.
  */
void I3OverlayNetwork::insertedTrigger(I3Trigger *trig, cbk_packet * dummy) {

    if (trig->trigInsertTimer != NULL) {
        trig->trigInsertTimer->setDisabled();
        trig->trigInsertTimer = NULL;
    }

    //reset the number of insert attempts made.
    trig->insertAttemptsCount = 0;
    
    if ( trig->isAnycast()) {
       I3_PRINT_DEBUG0 (
               DEBUG_LEVEL_MINIMAL,
               "Inserted Anycast trigger.\n"
            );
    } else if ( trig->isPublic ()) {
        // public trigger was inserted.
      
        PublicTrigger *pubTrig = (PublicTrigger *) trig;
        I3_PRINT_INFO1(INFO_LEVEL_MINIMAL, "Public trigger inserted for %s\n", pubTrig->i3DNS.c_str());
        internalState = I3_COMPLETED_INIT;      //TODO We are not considering multiple public triggers here

#ifdef _WIN32
		char tmpMsgBuf[MAX_BUF_SIZE];
		snprintf (tmpMsgBuf, MAX_BUF_SIZE, "Public trigger inserted for %s.\n", pubTrig->i3DNS.c_str());
		setMonitor_BalloonMsg (string (tmpMsgBuf));
#endif
    } else {
        // private trigger was inserted.
       
        I3_PRINT_DEBUG1(
                DEBUG_LEVEL_MINIMAL, 
                "Private trigger with id %s inserted\n", 
                sprintf_i3_id(tmpBuf, &trig->cl_trig->t->id)
            );

        I3OCDStateInfo* i3OCDState = trig->ocdState;
        if (i3OCDState != NULL) {
	    
            // Remember that the local private trigger for this VN has been inserted.
            i3OCDState->localPrivTrigInsertedFlag = true;
                
	        // We cannot directly set the status of the VN to ready once the local private 
	         // trigger has been inserted, even while in crash recovery and we already know
	        // the remote private id of the remote end point.  We again send a request
	        // for a remote private id.  If the remote end point is alive, it will notice that
	        // it has already inserted a private trigger for us and send us back the same thing.
	        // Otherwise, it will send us a new remote private id.
	   
            if (i3OCDState->state == FAKE_STATE_NEW 
                        || i3OCDState->state == FAKE_STATE_RENEW 
                        || i3OCDState->state == FAKE_STATE_NEW_VIA_SERVER_PROXY
                        ) {
                // We have inserted our private trigger, now request a private trigger from
                // the remote server.  All this happens when we act as a client trying to contact
                // a remote server.

		        // Initialize the count of number of private trigger requests made so far.
		        i3OCDState->numRemotePrivateIdRequests = 0;
		        requestPrivateTrigger(i3OCDState);
                
                
            } else if (i3OCDState->state == FAKE_STATE_CNF || i3OCDState->state == FAKE_STATE_RECNF) {
                    //We had inserted a private trigger on request from a remote sender. 
                    //The insertion of that private trigger has been acked.  Now we send 
                    //a private trigger confirmation to the original sender.
                    
                    I3_PRINT_DEBUG1 (
				        I3_DEBUG_LEVEL_VERBOSE, 
				        "Sending private trigger confirmation at %s\n", 
				        sprintf_i3_id(tmpBuf, &i3OCDState->prv_id)
			        );

        
                    cl_buf* clb = NULL;
                
                    clb = packPrivateTriggerConfirmation (i3OCDState, &(i3OCDState->prv_id));
                

                    // We are now ready to relay data
                    // Note that the relevant security state might not have been completely set up yet.
                    // TODO is this okay?
                    
                    i3OCDState->state = FAKE_STATE_OK;
                    i3OCDState->status = LAYER_INFO_STATUS_READY;
                    addRemoteVN(i3OCDState);


                    context->sendToStack (
                                            &(i3OCDState->remotePrivateIdStack), 
                                            clb, 
                                            enableShortcut?CL_PKT_FLAG_ALLOW_SHORTCUT:0
                                        );

                    I3_PRINT_DEBUG1 (
				        I3_DEBUG_LEVEL_MINIMAL, 
				        "Sending private trigger confirmation to %s\n", 
				        sprintf_i3_id(tmpBuf, &i3OCDState->remotePrivateId->id)
			        );
            }
        } else {
            I3_PRINT_INFO0(
			    I3_INFO_LEVEL_WARNING, 
			    "\nNo Virtual Node associated with confirmed private trigger.\n"
		    );
        }
    }
}

/** 
  * This function is called when the proxy receives a confirmation for a private trigger it had requested to a remote host.  Please note that this is completely different from the event of receiving an ack from the overlay on the insertion of a local private trigger.
  * @param senderPrivId The private of ID of the remote node sending the confirmation
  * @param myID The id of the node for which this confirmation was meant.
  * @returns The I3OCDStateInfo for which this private trigger confirmation was sent.
  */
I3OCDStateInfo* I3OverlayNetwork::handlePrivateTriggerConfirmation(
        ID* myId, const I3Block *blk
    ) {

    // Find the I3OCDStateInfo associated to which this private trigger confirmation was sent, i.e. myself. */
    I3OCDStateInfo *ocdState = lookupLocalVN(*myId);
  
    const I3PrivateTriggerReply *i3ReplyBlk = (I3PrivateTriggerReply *) blk;

    const ID* senderPrivId = &i3ReplyBlk->privTrigId;
   
    if (ocdState != NULL) {

        I3_PRINT_DEBUG2(
                DEBUG_LEVEL_MINIMAL, 
                "Received Private Trigger confirmation from %s (%s).\n", 
                ocdState->endPointName, sprintf_i3_id(tmpBuf, senderPrivId)
            );

		
        // We have successfuly obtained a private trigger request confirmation.
        // Reset the number of attempts to 0 so that we can start afresh next time 
        // (for eg: after a crash)
        ocdState->numRemotePrivateIdRequests = 0;
        
	    if (ocdState->prvTriggerConfirmationTimer != NULL) {
	        // We have received the private trigger confirmation from
	        // the remote end point of this VN.  Turn off the private trigger
	        // confirmation timer.
	    
	        ocdState->prvTriggerConfirmationTimer->setDisabled();
                ocdState->prvTriggerConfirmationTimer = NULL;
	    }

        //We are overwriting the originial remote id , i.e. the public id.
        //TODO QQQ: Should we keep a copy of the public Id somewhere?
        if ( ocdState->state == FAKE_STATE_NEW || ocdState->remotePrivateId == NULL) {
            ocdState->remotePrivateId = new I3Id();
        }

        // Please note that a private id diffferent from the one we
        // had obtained previously (if this is not the first time)
        // may be contained in the private trigger request confirmation.
        // We use the trigger in the latest reply.
        init_i3_id(&ocdState->remotePrivateId->id, senderPrivId);
        ocdState->remotePrivateIdStack.len = 1;
        ocdState->remotePrivateIdStack.ids = &ocdState->remotePrivateId->id;
        
        if ( ocdState->state == FAKE_STATE_NEW ) {
            
            char *optionBlocks = NULL;
            
            int optionLen = 0;
            if (blk->piggybackLen > 0) {
                
                //piggyback blocks are present
                I3Block* tmpI3Blk = (I3Block *) (((char *) blk) + sizeof (I3PrivateTriggerReply));
                if (tmpI3Blk->blockType == I3_BLOCK_GENERIC) {
                    I3GenericBlock *tmpI3GenericBlock = (I3GenericBlock *) tmpI3Blk;
                    optionBlocks = (char *) tmpI3GenericBlock->genericBlock;
                    optionLen = tmpI3GenericBlock->headerBlock.nextBlock - sizeof (I3GenericBlock);
                } 
            } 
 
            addRemoteVN(ocdState);
        }

            
        ocdState->state = FAKE_STATE_OK;
        ocdState->status = LAYER_INFO_STATUS_READY;
        delPrivTrigReqOCDState (ocdState->remotePublicId->id);
        ocdState->notifySetupSuccess();


	    if (ocdState->crashRecoveryInProgress) {
		        I3_PRINT_DEBUG1( 
				    DEBUG_LEVEL_MINIMAL, 
				    "CrashRecovery: Changing status of I3 OCD State (%s) to OK\n", 
				    ocdState->endPointName
			    );
        }
    } else {
            I3_PRINT_DEBUG0(I3_DEBUG_LEVEL_MINIMAL, "Received private trigger confirmation on non existent virtual node!\n");
    }

    return ocdState;
}


/**
  * This function is called whenever a remote client contacts this i3_client requesting a private trigger.
  * @param senderPrivId The private id of the sender.
  * @param myID My public id - the id on which the remote client contacted this i3 client.
  * @param enableFastConnectionSetup Did the other end point request fast connection setup?
  * @return The virtual node for which this private trigger request was meant.
  * If the virtual node already existed, it is returned.  Else the newly created Virtual node is returned.
  */
I3OCDStateInfo* I3OverlayNetwork::handlePrivateTriggerRequest(
                                            ID* myId, 
                                            ID* senderPrivId,
                                            bool tmpEnableFastConnectionSetup, //param name prefixed with tmp as there already exists enableFastConnectionSetup in class I3OverlayNetwork
                                            I3Block *reqBlock,
                                            const cbk_packet *cbk_pkt

                                        ) {
    
    static cl_buf       *clb = NULL;
    if (NULL == clb) {
        clb = cl_alloc_buf(I3_PROXY_HDR_LEN);
    }

   
    
    I3_PRINT_DEBUG1(DEBUG_LEVEL_SUPER, "Handling Private Trigger request from %s.\n",
		    sprintf_i3_id(tmpBuf, senderPrivId));
  
    I3_PRINT_DEBUG2(DEBUG_LEVEL_SUPER, "Handling Private Trigger request at %s. stacklen = %d\n",
		    sprintf_i3_id(tmpBuf, myId),
            cbk_pkt->stack_len);
    
    I3OCDStateInfo * ocdState = lookupRemoteVN(*senderPrivId);

    if (ocdState == NULL) {
        I3_PRINT_DEBUG0 ( 
			DEBUG_LEVEL_SUPER,
 			"Private trigger request from previously UNKNOWN host.\n"
		    );
        //This remote sender is unknown, i.e. we haven't seen him before,
        //atleast on senderPrivId.
        //So create a new OCDState for senderPrivId.

        // Create new OCD state
        LayerHandle i3Handle = getNewOCDHandle();
        ocdState = new I3OCDStateInfo (i3Handle, oci, this);
        putStateInfo (i3Handle, ocdState);
        ocdState->i3VNType = I3_VN_TYPE_REMOTE_CREATED;

        //add to the list of OCD states
	addOCDState (ocdState);
        
        memcpy ( &ocdState->contactId, myId, sizeof(ID));
        
        ocdState->remotePrivateId = new I3Id();
        init_i3_id(&ocdState->remotePrivateId->id, senderPrivId);

        // copy the remainder of the trigger stack, if any
        // If there is any remainder, it means that it is the next hop.
        // This next hop is known only at the OCD level.
        // This will be revealed to the OCI layer when it asks for it.
        for (int i = 0; i < cbk_pkt->stack_len; i++) {
            memcpy (&ocdState->triggerStack[i], &cbk_pkt->ids[i], sizeof (ID));
           
            I3_PRINT_DEBUG1 (
                            DEBUG_LEVEL_SUPER,
                            "Setting the remainder of the stack on private trigger request: %s.\n",
                            sprintf_i3_id (tmpBuf, &cbk_pkt->ids[i])
                        );
        }
        ocdState->triggerStackLen = cbk_pkt->stack_len;
        
        //Did the remote VN request a fast setup
        ocdState->enableFastConnectionSetup = tmpEnableFastConnectionSetup;
        
        char *optionBlocks = NULL;
        int optionLen = 0;
        if (reqBlock->piggybackLen > 0) {
            // piggy back blocks are present!
            I3Block* tmpI3Blk = (I3Block *) (((char *) reqBlock) + sizeof (I3PrivateTriggerRequest));
            if (tmpI3Blk->blockType == I3_BLOCK_GENERIC) {
                I3GenericBlock *tmpI3GenericBlock = (I3GenericBlock *) tmpI3Blk;
                optionBlocks = (char *) tmpI3GenericBlock->genericBlock;
                optionLen = tmpI3GenericBlock->headerBlock.nextBlock - sizeof (I3GenericBlock);
            } 
        } 
        
        
        ocdState->addr_type = I3_ADDR_TYPE_IPv4;

        //Initialize the state of the VN, insert new private trigger.
        //Processing continues when the ack for this trigger insertion comes back.
        //Then we send a private trigger confirmation to remote sender.
        initFakeState(ocdState, FAKE_STATE_CNF, (PublicTrigger *) lookupTrigger (*myId));

        // Set the address stack to which the trigger confirmation will be sent after inserted.
        // This is the i3 id of the node requesting the private trigger, i.e senderPrivId
        ocdState->remotePrivateIdStack.len = 1;
        ocdState->remotePrivateIdStack.ids = &ocdState->remotePrivateId->id;

        addRemoteVN(ocdState);

        if (ocdState->enableFastConnectionSetup) {
                //TODO connection has been set up.
        }
                                
        //Processing continues when private trigger insertion (done by initFakeState()) is confirmed.
         
    } else {

        I3_PRINT_DEBUG0 (
			DEBUG_LEVEL_SUPER,
			"Private trigger request from previously KNOWN end point\n"
		     );
	
        //This is a request from a known end point.  A private trigger for
	    //this end point had been inserted earlier.
        //TODO We have to make sure that the private trigger has already been inserted when
        //this request from a known end point is received.
	
        //TODO check if the original trigger is still in i3.
	    //This is necessary when I3VNs are killed or maybe if I3VNs are killed,
	    //we won't reach this else part in the first place!  TODO
	
	    //Just send back the original private id.
        
	    //TODO get rid of this frequent memory alloc and dealloc by preallocating some buffers

	if ( ocdState->localPrivTrigInsertedFlag) {
	    // The local private trigger has already been inserted.  So we can send 
            // private trigger confirmation straight away.
	     	cl_buf* clb;
        	clb = packPrivateTriggerConfirmation (ocdState, &(ocdState->prv_id) );
       	 	context->sendToStack (&(ocdState->remotePrivateIdStack), clb, enableShortcut?CL_PKT_FLAG_ALLOW_SHORTCUT:0);
        } else {
            // There is a pending private trigger request for this ocd state.
            // So ignore this one.
        }
        
    }

    return ocdState;
}

/**
  * This function is called back from the I3ClientContext when some event occurs on the specified trigger
  * other than trigger insertion ack.
  * Note that a trigger insertion event and other events identified by the options in the i3 header are 
  * handled separately.
  * @param trig The trigger on which the packet was received.
  * @param pkt The contents of the received packet.
  */
void I3OverlayNetwork::receivedTriggerEvent( I3Trigger *trig, cbk_packet *pkt ) {
  
    if (trig == NULL) {
       I3_PRINT_DEBUG0 (
                      DEBUG_LEVEL_SUPER,
                      "Trigger null in received trigger event.  This should not happen!!!\n"
                   );
    }
 
    ID id_ret;
    int payload_len = pkt->clb->data_len;

    init_i3_id (&id_ret, &(trig->i3Id->id));

    I3_PRINT_DEBUG1 (DEBUG_LEVEL_VERBOSE, "\nRECEIVED TRIGGER EVENT on ID: %s.\n", sprintf_i3_id(tmpBuf, &(id_ret)));

    if (payload_len) {

        int currBlockPos = 0;
      
        // The generic block should come only after the i3 specific blocks.
        // This is an absolute necessity
      
        // This is the virtual node on which the i3 specific blocks
        // act.  The pointer is here because the generic part needs to 
        // know the virtual node on which it must act and only the i3
        // specific part can retrieve the VN (based on private id)
        I3OCDStateInfo* ocdState = NULL;

        // Process each block present in the received packet.
        while (1) {
 
            
            const I3Block *currBlock = (I3Block *) (pkt->clb->data + currBlockPos);
            
            //Process the current block
            switch (currBlock->blockType) {
              
                
                case I3_BLOCK_GENERIC_VIA_PUB_TRIG:

                    I3_PRINT_DEBUG0 (I3_DEBUG_LEVEL_MINIMAL,
                            "I3_BLOCK_GENERIC_VIA_PUB_TRIG\n"
                        );
                    ocdState = lookupRemoteVN( ( (I3GenericBlockViaPubTrig *) currBlock)->senderPrivId);
                    
                    if (ocdState != NULL) {
                    
                        // This is a generic block intended for the OCI
                        I3GenericBlockViaPubTrig *genBlk = (I3GenericBlockViaPubTrig *) currBlock;


                        assert (ocdState->oci != NULL) ;
                        ocdState->oci->receive ( (char *) genBlk->genericBlock, this, ocdState->myHandle);
        
#ifdef BENCHMARK
	addCheckpoint ( EVENT_DATA_OC_D_TO_OC_I, 1);
#endif                
                        //TODO ocdState->prevLayer->receive ( (char *) tmpBlk, tmpBlk->nextBlock);
                        
                        //TODO KILLLINEocdState->processBlocks ( (ProxyPacketBlock *) genBlk->genericBlock);
                    }  else {
                        I3_PRINT_DEBUG0 (
                                I3_DEBUG_LEVEL_MINIMAL,
                                "Unable to process generic blocks on a NULL Virtual Node\n"
                            );
                    }
    
                break;

                case I3_BLOCK_GENERIC:
                {

                    I3_PRINT_DEBUG0 ( I3_DEBUG_LEVEL_VERBOSE,
                            "I3_BLOCK_GENERIC\n"
                        );

                    // This is a generic block intended for the OCI.
                    const I3GenericBlock *genBlk = (I3GenericBlock *) currBlock;

                    if (ocdState == NULL) {
                        // This will happen if there was no i3 specific block
                        // prior to this block.  For example, when a standalone
                        // generic OCI control block is sent.

                        // So retrieve the VN associated with the id on which
                        // the packet was received.  This VN should exist
                        // as standalone packets are sent only after connection is
                        // established.
                        ocdState = lookupLocalVN (id_ret);
                    }

                    if (ocdState != NULL) {
                        
#ifdef BENCHMARK
	addCheckpoint ( EVENT_DATA_OC_D_TO_OC_I, 1);
#endif              
                        ocdState->oci->receive ( (char *) genBlk->genericBlock, this, ocdState->myHandle);;
                        
                    }  else {
                        I3_PRINT_DEBUG0 (
                                I3_DEBUG_LEVEL_MINIMAL,
                                "Unable to process generic blocks on a NULL Virtual Node\n"
                            );
                    }
                        
                }
                break;
                        
                case I3_BLOCK_PUBLIC_TRIG_CNF:  //TODO
                    I3_PRINT_DEBUG0 (I3_DEBUG_LEVEL_MINIMAL, "I3_BLOCK_PUBLIC_TRIG_CNF\n");
                    //handle_public_trigger_confirmation (buf + 2, &id_ret);
                break;
            
                case I3_BLOCK_PRIVATE_TRIG_REMOVE: //TODO
                    I3_PRINT_DEBUG0 (I3_DEBUG_LEVEL_MINIMAL, "I3_BLOCK_PRIVATE_TRIG_REMOVE\n");
                    //handle_trigger_remove (&id_ret);
                break;
            
                case I3_BLOCK_FAKE_REMOVE: //TODO
                    I3_PRINT_DEBUG0 (I3_DEBUG_LEVEL_MINIMAL, "I3_BLOCK_FAKE_REMOVE\n");
                    //handle_fake_remove (&id_ret);
                break;
    
                case I3_BLOCK_PRIVATE_TRIG_REQ:
                {
                    I3_PRINT_DEBUG0 (I3_DEBUG_LEVEL_MINIMAL, "I3_BLOCK_PRIVATE_TRIG_REQ\n");
                    I3PrivateTriggerRequest *tmpBlk = (I3PrivateTriggerRequest *) currBlock;
                    ocdState = handlePrivateTriggerRequest (
                                            &id_ret, 
                                            &tmpBlk->senderPrivId, 
                                            tmpBlk->enableFastConnectionSetup,
                                            (I3Block *)tmpBlk,
                                            pkt
                                        );
                }
                break;

                case I3_BLOCK_PRIVATE_TRIG_CNF:
                    I3_PRINT_DEBUG0 (I3_DEBUG_LEVEL_MINIMAL, "I3_BLOCK_PRIVATE_TRIG_CNF\n");
                    ocdState = handlePrivateTriggerConfirmation (&id_ret, (I3Block *) currBlock);
                break;

                case I3_BLOCK_LEGACY_DNS_ERROR: //TODO
                    I3_PRINT_DEBUG0 (I3_DEBUG_LEVEL_MINIMAL, "I3_BLOCK_LEGACY_DNS_ERROR\n");
                    //handle_legacy_dns_answer (buf + 2, payload_len - 2, &id_ret);
                break;

                default:
                    I3_PRINT_DEBUG1 (
                        DEBUG_LEVEL_MINIMAL,
                         "Unknown block type %d received.\n",
                         currBlock->blockType
                        );
                break;
                
            }
            
            if (currBlock->nextBlock == 0) { 
                //No more blocks to process.
                break;
            } else {
                currBlockPos += currBlock->nextBlock;
            }
        }
        
    }

}

/**
  * This function is called when a trigger to which a packet was sent
  * does not exist.
  * @param trigId The ID of the trigger that does not exist.
  */
void I3OverlayNetwork::processTriggerNotFound(ID *trigId) {
  
    assert (trigId != NULL);
    
    I3_PRINT_INFO1(
            I3_INFO_LEVEL_WARNING, 
            "A TRIGGER NOT FOUND option was received for trigger (%s).\n",
            sprintf_i3_id(tmpBuf, trigId)
        );
   
    I3OCDStateInfo *tmpOCDState;
    
    // Check if we had made a private trigger request to this trig id 
    // which is not found.
    tmpOCDState = lookupPrivTrigReqMap (*trigId);
    if (tmpOCDState != NULL) {
        // This trigger not found message is for a private trigger request
        // we had made.
        // Notify setup failure to any waiting OCIs.
        tmpOCDState->notifySetupFailure();

        return;
    }
    
    tmpOCDState = lookupRemoteVN(*trigId);
    if (tmpOCDState != NULL) {
            
        if (tmpOCDState->state == FAKE_STATE_OK  && tmpOCDState->i3VNType == I3_VN_TYPE_LOCAL_CREATED) {
            // i3 connection has once established, but
            // partner's private trigger no longer exists.
            // send private trigger request again to re-establish i3 connection.
    
            if (tmpOCDState->numRemotePrivateIdRequests 
                    > I3_MAX_NUM_REMOTE_PRIVATE_ID_REQUESTS) {
	            // Already made enough attepts to obtain a private id 
                // from the remote host. We give up!
	            I3_PRINT_INFO2( 
                        I3_INFO_LEVEL_WARNING, 
                        "\n Tried to obtain private trigger for %s %d times."//
                        "I give up!\n",
                        tmpOCDState->endPointName, 
                        I3_MAX_NUM_REMOTE_PRIVATE_ID_REQUESTS
                    );
                tmpOCDState->notifySetupFailure();
                
	        } else {
                    I3_PRINT_INFO2( 
                      	I3_INFO_LEVEL_VERBOSE, 
                       	"\n Unable to find the private trigger associated with %s."//
                       	"Trying again %d\n", 
                       	tmpOCDState->endPointName, 
                       	tmpOCDState->numRemotePrivateIdRequests
                    );
                
        	    requestPrivateTrigger(tmpOCDState);            
		    }
        }
    } else {
        I3_PRINT_DEBUG1(
            I3_DEBUG_LEVEL_MINIMAL, 
            "Virtual node with the removed trigger %s as the remote"//
            "end point not found!\n", 
            sprintf_i3_id(tmpBuf,trigId)
        );
    }
}

